﻿using CommonEngine;
using SqlSugar;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;

namespace CommonDevelop
{
    /// <summary>
    /// 数据库启用器(HOF)
    /// </summary>
    public class SugarBizDB
    {
        private static ConcurrentDictionary<string, ConnectionConfig> _cache = new ConcurrentDictionary<string, ConnectionConfig>();
        private static ThreadLocal<string> _threadLocal;
        private static ThreadLocal<string> _threadLocalReadOnly;

        static SugarBizDB()
        {
            _threadLocal = new ThreadLocal<string>();
            _threadLocalReadOnly = new ThreadLocal<string>();
        }

        private static SqlSugarClient CreatInstance()
        {
            ConnectionConfig RO = null;
            var config = ConfigHelper.GetSugarSettings(ref RO);
            if (config == null)
                throw new AdminCommonException("没有配置SugarConfig环境参数");

            SqlSugarClient client = new SqlSugarClient(config);
            var key = Guid.NewGuid().ToString("N");
            var keyRO = Guid.NewGuid().ToString("N");
            if (!_cache.ContainsKey(key))
            {
                _cache.TryAdd(key, config);
                _threadLocal.Value = key;
                _threadLocalReadOnly.Value = keyRO;

                if (RO != null)
                    _cache.TryAdd(keyRO, RO);

                return client;
            }
            else
                throw new AdminCommonException("创建SqlSugarClient失败");
        }
        public static SqlSugarClient GetInstance()
        {
            var id = _threadLocal.Value;
            if (string.IsNullOrEmpty(id) || !_cache.ContainsKey(id))
                return CreatInstance();
            else
            {
                var config = _cache[id];
                SqlSugarClient client = new SqlSugarClient(config);
                return client;
            }
        }
        public static SqlSugarClient GetReadOnlyInstance()
        {
            var id = _threadLocalReadOnly.Value;
            if (string.IsNullOrEmpty(id) || !_cache.ContainsKey(id))
                return CreatInstance();
            else
            {
                var config = _cache[id];
                SqlSugarClient client = new SqlSugarClient(config);
                return client;
            }
        }

        public static T GetReadOnlyOne<T>(object keyValue)
            where T : class, new()
            => GetReadOnlyInstance().Queryable<T>().With(SqlWith.NoLock).InSingle(keyValue);

        public static T GetReadOnlyOne<T>(Expression<Func<T, bool>> express)
            where T : class, new()
            => GetReadOnlyInstance().Queryable<T>().With(SqlWith.NoLock).First(express);

        public static IEnumerable<T> GetReadOnlyMore<T>(Expression<Func<T, bool>> express)
            where T : class, new()
            => GetReadOnlyInstance().Queryable<T>().With(SqlWith.NoLock).Where(express).ToList();

        public static bool Add<T>(T En, bool Trans)
            where T : class, new()
        {
            if (En == null)
                return false;

            var DB = SugarBizDB.GetInstance();
            if (Trans)
            {
                try
                {
                    DB.BeginTran();
                    DB.Insertable<T>(En).IgnoreColumns(true).ExecuteCommand();
                    DB.CommitTran();
                }
                catch (Exception e)
                {
                    DB.RollbackTran();
                    throw e;
                }
            }
            else
            {
                DB.Insertable<T>(En).IgnoreColumns(true).ExecuteCommand();
            }
            return true;
        }

        public static bool Update<T>(T En, bool Trans)
            where T : class, new()
        {
            if (En == null)
                return false;

            var DB = SugarBizDB.GetInstance();
            if (Trans)
            {
                try
                {
                    DB.BeginTran();
                    DB.Updateable<T>(En).ExecuteCommand();
                    DB.CommitTran();
                }
                catch (Exception e)
                {
                    DB.RollbackTran();
                    throw e;
                }
            }
            else
                DB.Updateable<T>(En).ExecuteCommand();

            return true;
        }

        public static bool Delete<T>(params long[] keyValue)
            where T : class, new()
        {
            if (keyValue.IsNullOrEmpty())
                return false;

            var DB = SugarBizDB.GetInstance();
            try
            {
                DB.BeginTran();
                DB.Deleteable<T>().In(keyValue).ExecuteCommand();
                DB.CommitTran();
            }
            catch (Exception e)
            {
                DB.RollbackTran();
                throw e;
            }
            return true;
        }
        public static bool Delete<T>(params string[] keyValue)
            where T : class, new()
        {
            if (keyValue.IsNullOrEmpty())
                return false;

            var DB = SugarBizDB.GetInstance();
            try
            {
                DB.BeginTran();
                DB.Deleteable<T>().In(keyValue).ExecuteCommand();
                DB.CommitTran();
            }
            catch (Exception e)
            {
                DB.RollbackTran();
                throw e;
            }
            return true;
        }
        public static bool Delete_Batch<T>(Expression<Func<T, bool>> expression)
            where T : class, new()
        {
            if (expression == null)
                return false;

            var DB = SugarBizDB.GetInstance();
            try
            {
                DB.BeginTran();
                DB.Deleteable<T>().Where(expression).ExecuteCommand();
                DB.CommitTran();
            }
            catch (Exception e)
            {
                DB.RollbackTran();
                throw e;
            }
            return true;
        }

        /// <summary>
        /// 单表数据保存
        /// </summary>
        public static bool SaveEntity<T>(T En) where T : class, new()
        {
            if (En == null)
                return false;

            var DB = GetInstance();
            try
            {
                DB.BeginTran();
                DB.Deleteable<T>(En).ExecuteCommand();
                DB.Insertable<T>(En).IgnoreColumns(true).ExecuteCommand();
                DB.CommitTran();
            }
            catch
            {
                DB.RollbackTran();
                return false;
            }
            return true;
        }

        /// <summary>
        /// 分页查询(高效存储过程)
        /// </summary>
        public static IEnumerable<T> GetPageProcedure<T>(Expression<Func<T, bool>> WhereFunc,
            bool SortDESC = false,
            Expression<Func<T, object>> OrderProperty = null)
            where T : class, new()
        {
            var DB = GetInstance();
            var TP = Get_PrimaryKey<T>();
            var IDKey = TP.PrimaryName;

            var OrderKey = OrderProperty == null ? null : ReflectionFactory.GetPropertyName(OrderProperty);
            var where = WhereFunc == null ? null : ExpressionHelper.GetSqlByExpression(WhereFunc.Body);

            var PCount = new SugarParameter("@PCount", null, true);
            var RCount = new SugarParameter("@RCount", null, true);
            var sys_Table = new SugarParameter("@sys_Table", SugarBizDB.GetTableName<T>());
            var sys_Key = new SugarParameter("@sys_Key", IDKey);
            var sys_Fields = new SugarParameter("@sys_Fields", "*");
            var sys_Where = new SugarParameter("@sys_Where", where);
            var sys_Order = new SugarParameter("@sys_Order", OrderKey);
            var sys_Sort = new SugarParameter("@sys_Sort", SortDESC ? 1 : 0);
            var sys_PageIndex = new SugarParameter("@sys_PageIndex", 2);
            var sys_PageSize = new SugarParameter("@sys_PageSize", 50);

            return DB.Ado.UseStoredProcedure().SqlQuery<T>("sys_Page_v2",
                PCount,
                RCount,
                sys_Table,
                sys_Key,
                sys_Fields,
                sys_Where,
                sys_Order,
                sys_Sort,
                sys_PageIndex,
                sys_PageSize);
        }

        public static string GetId()
        {
            var id = _threadLocal.Value;
            if (string.IsNullOrEmpty(id))
                throw new AdminCommonException("内部错误: SqlSugarClient已丢失.");
            else
                return id;
        }
        public static string GetReadOnlyId()
        {
            var id = _threadLocalReadOnly.Value;
            if (string.IsNullOrEmpty(id))
                throw new AdminCommonException("内部错误: SqlSugarClient已丢失.");
            else
                return id;
        }

        /// <summary>
        /// 获取实体对象Key
        /// </summary>
        public static (string PrimaryName, Type PrimaryType) Get_PrimaryKey<T>() where T : class
        {
            string name = Get_PrimaryKey(typeof(T), out Type t);
            return (name, t);
        }
        public static Expression<Func<T, bool>> Make_PrimaryKeyExp<T>(object Val)
            where T : class
        {
            if (Val.IsEmpty())
                Val = "0";

            var TP = Get_PrimaryKey<T>();
            if (TP.PrimaryName.IsEmpty() || Val.IsEmpty())
                return (a => false);

            Type t = TP.PrimaryType;

            MethodInfo mo = typeof(object).GetMethod("Equals", new Type[] { typeof(object) });
            if (t == typeof(long))
            {
                mo = typeof(long).GetMethod("Equals", new Type[] { typeof(long) });
                Val = long.Parse(Val.ToString());
            }
            else if (t == typeof(int))
            {
                mo = typeof(int).GetMethod("Equals", new Type[] { typeof(int) });
                Val = int.Parse(Val.ToString());
            }
            else if (t == typeof(string))
                mo = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });

            ParameterExpression left = Expression.Parameter(typeof(T), "c");
            Expression expression = Expression.Constant(false);
            Expression right = Expression.Call(Expression.Property(left, typeof(T).GetProperty(TP.PrimaryName)),
                mo,
                Expression.Constant(Val));

            expression = Expression.Or(right, expression);
            Expression<Func<T, bool>> finalExpression = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
            return finalExpression;
        }
        public static Expression<Func<Dto, bool>> Make_DtoKeyExp<T, Dto>(object Val)
            where T : class
            where Dto : class, new()
        {
            if (Val.IsEmpty())
                Val = "0";

            var TP = Get_PrimaryKey<T>();
            if (TP.PrimaryName.IsEmpty() || Val.IsEmpty())
                return (a => false);
            
            Type t = TP.PrimaryType;

            MethodInfo mo = typeof(object).GetMethod("Equals", new Type[] { typeof(object) });
            if (t == typeof(long))
            {
                mo = typeof(long).GetMethod("Equals", new Type[] { typeof(long) });
                Val = long.Parse(Val.ToString());
            }
            else if (t == typeof(int))
            {
                mo = typeof(int).GetMethod("Equals", new Type[] { typeof(int) });
                Val = int.Parse(Val.ToString());
            }
            else if (t == typeof(string))
                mo = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });

            ParameterExpression left = Expression.Parameter(typeof(Dto), "c");
            Expression expression = Expression.Constant(false);
            Expression right = Expression.Call(Expression.Property(left, typeof(Dto).GetProperty(TP.PrimaryName)),
                mo,
                Expression.Constant(Val));

            expression = Expression.Or(right, expression);
            Expression<Func<Dto, bool>> finalExpression = Expression.Lambda<Func<Dto, bool>>(expression, new ParameterExpression[] { left });
            return finalExpression;
        }
        public static string Get_PrimaryKey(Type type, out Type PT)
        {
            PropertyInfo[] props = type.GetProperties();
            foreach (PropertyInfo prop in props)
            {
                PT = prop.PropertyType;
                foreach (Attribute attr in prop.GetCustomAttributes(true))
                {
                    SugarColumn keyattribute = attr as SugarColumn;
                    if (keyattribute != null && keyattribute.IsPrimaryKey)
                        return prop.Name;
                }
            }
            PT = null;
            return string.Empty;
        }

        /// <summary>
        /// 获取实体主键的值
        /// </summary>
        public static Type Get_PrimaryKeyValue<T>(T enValue, out long Id, out string No) where T : class
        {
            Id = 0;
            No = string.Empty;
            if (enValue == null)
                return null;

            var TP = Get_PrimaryKey<T>();
            string propertyName = TP.PrimaryName;
            Type t = TP.PrimaryType;

            object Val = null;
            PropertyInfo[] ps = enValue.GetType().GetProperties();
            foreach (PropertyInfo p in ps)
            {
                if (p.Name == propertyName)
                {
                    Val = p.GetValue(enValue, null);
                    if (t == typeof(long))
                        Id = Convert.ToInt64(Val);
                    else
                        No = Val == null ? string.Empty : Val.ToString();

                    break;
                }
            }
            return t;
        }
        /// <summary>
        /// 设置实体主键的值
        /// </summary>
        public static T Set_PrimaryKeyValue<T>(object keyValue, T En = null) where T : class
        {
            if (keyValue == null)
                return default(T);

            try
            {
                if (En == null)
                    En = ReflectionFactory.GreateByType<T>();

                var TP = Get_PrimaryKey<T>();
                ReflectionFactory.SetValue(En, keyValue, TP.PrimaryName);
                return En;
            }
            catch
            {
                return default(T);
            }
        }
        /// <summary>
        /// 获取视图主键的值
        /// </summary>
        public static object Get_DtoKeyValue<T>(object Dto) where T : class
        {
            if (Dto == null)
                return null;

            try
            {
                var TP = Get_PrimaryKey<T>();
                return ReflectionFactory.GetPropertyValue(Dto, TP.PrimaryName);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 获取实体代表的表名
        /// </summary>
        public static string GetTableName(Type entityType)
        {
            var tablename = entityType.GetCustomAttributes(typeof(SugarTable), true);
            if (tablename != null && tablename.Length > 0)
                return ((SugarTable)tablename[0]).TableName;
            else
                return entityType.Name;
        }
        public static string GetTableName<T>() where T : class
        {
            return GetTableName(typeof(T));
        }


        /// <summary>
        /// 生成序号 exp: ABC00000001
        /// </summary>
        private static string PickNew(string TableName, string prefix, int numlong = 8)
        {
            if (string.IsNullOrEmpty(TableName))
                return null;

            if (!TableName.Contains("Seq"))
                return null;

            try
            {
                var DB = GetInstance();
                string SQL = string.Format("INSERT INTO {0}(Seq)VALUES(1);SELECT Id FROM {0};DELETE {0};", TableName);
                DB.AddQueue(SQL);
                var result = DB.SaveQueues<long>(false);
                long bn_id = (long)Convert.ChangeType(result.FirstOrDefault(), typeof(long));
                return string.Format("{0}{1}", prefix, bn_id.ToString(string.Concat(new string('0', numlong))));
            }
            catch
            {
                return null;
            }

            /*
             * IF EXISTS (SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME ='RBAC_Seq_Struct')
             * DROP TABLE RBAC_Seq_Struct
             * GO
             * ---生成Seq编号表---统一模板
             * CREATE TABLE RBAC_Seq_Struct(
             * [Id] BIGINT IDENTITY(1, 1) PRIMARY KEY , ---流水号--(自动)
             * [Seq] BIGINT NOT NULL DEFAULT ( 0 )
             * )
             * GO
             */
        }
        /// <summary>
        /// 基础信息（配置，组织结构，流程定义）--使用
        /// </summary>
        public static string BuildSeq_01(string prefix, int numlong = 8)
        {
            return PickNew("Seq_GetNo01", prefix, numlong);
        }
        /// <summary>
        /// 常用信息（附件，流程）--使用
        /// </summary>
        public static string BuildSeq_02(string prefix, int numlong = 8)
        {
            return PickNew("Seq_GetNo02", prefix, numlong);
        }
    }
}
